/*
 * Copyright (C) 2008 Peter Kling
 *
 * This file is part of 'A Kind of Billiard', a graphical billiard simulation.
 *
 * 'A Kind of Billiard' is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * 'A Kind of Billiard' is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * 'A Kind of Billiard'. If not, see <http://www.gnu.org/licenses/>.
 */

package cg2.models;

import java.io.File;
import java.io.IOException;

import javax.media.opengl.GL;

import com.sun.opengl.util.texture.Texture;
import com.sun.opengl.util.texture.TextureIO;

/**
 * @brief Data object that collects OpenGL material information and allows easy usage of materials in OpenGL.
 */
public class GLMaterial {
	private String name;
	private Texture texture   = null;
	private Texture normalMap = null;
	private float[] Ka;
	private float[] Kd;
	private float[] Ks;
	private float Ns;
	
	/**
	 * @brief Sets the OpenGL state to use this material.
	 */
	public void enable(GL gl) {
		enableNormalMap(gl);
		enableTexture(gl);
		if (Ka != null) gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT , Ka, 0);
		if (Kd != null) gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE , Kd, 0);
		if (Ks != null) gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, Ks, 0);
		gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, Ns);
		
		// XXX: HACKY
		// BE AWARE: The following code is due to communicate the enable-status of the texture unit to Cg shaders.
		//           Because Cg does not yet provides access to these OpenGL state variables, we misuse the OpenGL
		//           fog color to propagate the states. This does also affect the phong-per-pixel fragment shader.
		float[] c = new float[4];
		c[0] = (texture   == null) ? 0.f : 1.f;
		c[1] = (normalMap == null) ? 0.f : 1.f;
		gl.glFogfv(GL.GL_FOG_COLOR, c, 0);
	}
	
	/**
	 * @brief Enables texture unit 1 and binds the normal map of this material to it.
	 * 
	 * If the normal map of this material is null, texturing for texture unit 1 is disabled.
	 */
	public void enableNormalMap(GL gl) {
		gl.glActiveTexture(GL.GL_TEXTURE1);
		if (normalMap == null) {
			gl.glDisable(GL.GL_TEXTURE_2D);
		} else {
			// enable, bind and configure texture
			normalMap.enable();
			normalMap.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
			normalMap.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
			normalMap.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
			normalMap.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
		}
	}
	
	/**
	 * @brief Enables texture unit 0 and binds the texture of this material to it.
	 * 
	 * If the texture of this material is null, texturing for texture unit 0 is disabled.
	 */
	public void enableTexture(GL gl) {
		gl.glActiveTexture(GL.GL_TEXTURE0);
		if (texture == null) {
			gl.glDisable(GL.GL_TEXTURE_2D);
		} else {
			// enable, bind and configure texture
			texture.enable();
			texture.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
			texture.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
			texture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
			texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
		}
	}
	
	/**
	 * @brief Returns the ambient color of this material.
	 */
	public float[] getColor_ambient() {
		return Ka;
	}
	
	/**
	 * @brief Returns the diffuse color of this material.
	 */
	public float[] getColor_diffuse() {
		return Kd;
	}
	
	/**
	 * @brief Returns the specular color of this material.
	 */
	public float[] getColor_specuar() {
		return Ks;
	}
	
	/**
	 * @brief Gets the material name.
	 */
	public String getName() {
		return name;
	}
	
	/**
	 * @brief Gets the material shininess.
	 */
	public float getShininess() {
		return Ns;
	}
	
	/**
	 * @brief Sets the ambient color of this material.
	 */
	public void setColor_ambient(float[] Ka) {
		this.Ka = Ka;
	}
	
	/**
	 * @brief Sets the diffuse color of this material.
	 */
	public void setColor_diffuse(float[] Kd) {
		this.Kd = Kd;
	}
	
	/**
	 * @brief Sets the specular color of this material.
	 */
	public void setColor_specuar(float[] Ks) {
		this.Ks = Ks;
	}
	
	/**
	 * @brief Sets the material name.
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * @brief Sets the material shininess.
	 */
	public void setShininess(float Ns) {
		this.Ns = Ns;
	}
	
	/**
	 * @brief Sets the texture for this material.
	 */
	public void setNormalMap(Texture normalMap) {
		this.normalMap= normalMap;
	}
	
	/**
	 * @brief Sets the texture for this material by loading it from the given file.
	 */
	public void setNormalMap(String fileName) {
		try {
			normalMap = TextureIO.newTexture(new File(fileName), false);
		} catch(IOException e) {
			System.err.println("Warning: Could not load material texture: " + e.getLocalizedMessage());
		}
	}
	
	/**
	 * @brief Sets the texture for this material.
	 */
	public void setTexture(Texture texture) {
		this.texture = texture;
	}
	
	/**
	 * @brief Sets the texture for this material by loading it from the given file.
	 */
	public void setTexture(String fileName) {
		try {
			texture = TextureIO.newTexture(new File(fileName), false);
		} catch(IOException e) {
			System.err.println("Warning: Could not load material texture: " + e.getLocalizedMessage());
		}
	}
}